package com.xuecheng.content.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.content.model.dto.CourseBaseDTO;
import com.xuecheng.api.content.teacherplan.dto.TeachplanDTO;
import com.xuecheng.common.enums.common.CommonEnum;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.TeachPlanEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.convert.TeachPlanConvert;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.Teachplan;
import com.xuecheng.content.entity.TeachplanMedia;
import com.xuecheng.content.entity.ex.TeachPlanNode;
import com.xuecheng.content.mapper.TeachplanMapper;
import com.xuecheng.content.service.CourseBaseService;
import com.xuecheng.content.service.TeachplanMediaService;
import com.xuecheng.content.service.TeachplanService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 课程计划 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class TeachplanServiceImpl extends ServiceImpl<TeachplanMapper, Teachplan> implements TeachplanService {

    @Autowired
    private CourseBaseService courseBaseService;

    @Autowired
    private TeachplanMediaService mediaService;

    /*
        查询课程计划业务分析：
        1.判断关键数据
                courseId,companyId

        3.判断课程是否被删除
        4.判断是否时同一家机构
        5.根据id查询课程计划
        6.判断是否有课程计划
        7.如果有就形成树形结构
        8.封装数据返回
     */
    public TeachplanDTO queryTeacherPlanById(Long courseId, Long companyId) {

        //1.判断关键数据
        //        courseId,companyId
        //2.判断课程是否存在
        //3.判断课程是否被删除
        //4.判断是否时同一家机构

        CourseBaseDTO dto = checkCourseBase(courseId, companyId);
        //5.根据id查询课程计划
        TeachplanMapper baseMapper = this.getBaseMapper();
        List<TeachPlanNode> nodeList = baseMapper.selectByCourseId(courseId);
        //6.判断是否有课程计划
        if (CollectionUtils.isEmpty(nodeList)) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }
        //7.如果有就形成树形结构
        TeachPlanNode rootNode = nodeList.remove(0);
        createTreeNodes(rootNode, nodeList);
        //8.封装数据返回
        TeachplanDTO teachplanDTO = TeachPlanConvert.INSTANCE.node2dto(rootNode);

        return teachplanDTO;
    }

    /*
        添加或修改课程计划业务分析
        0.根据有无id判断是添加还是修改，
        1.判断关键数据
                pname,companyId

        3.判断课程是否被删除
        4.判断课程状态
        5.判断是否时同一家机构
        6.根据id查询课程计划
        7.获得父级节点数据
         8.将当前的课程计划和父级数据进行关联
         9.保存课程基本信息数据
           判断保存的结果
         10.将po数据转为dto并返回
    */
    @Transactional
    public TeachplanDTO modifyOrCreateTeachPlan(TeachplanDTO dto,Long companyId) {
        //根据有无id判断是添加还是修改
        if(ObjectUtils.isEmpty(dto.getTeachPlanId())){
            //无id -- 添加
           return createTeanchPlan(dto,companyId);
        }else{
            //有id -- 修改
           return modifyTeachPlan(dto,companyId);
        }

    }

    /*
        删除课程计划业务分析：
        1.判断关键数据teachPlanId,companyId
        2.判断计划是否存在
        3.判断计划是否删除、（因为是物理删除所有无需判断）
        4、判断是否是同一家机构
        5.判断课程等级
        6.判断2级下有无3级

       7.判断3级有无管理媒资数据
       8.删除数据

     */
    @Transactional
    public void deleteTeachPlan(Long teachPlanId, Long companyId) {
        //1.判断关键数据teachPlanId,companyId
        if(ObjectUtils.isEmpty(teachPlanId) || ObjectUtils.isEmpty(companyId)){
            ExceptionCast.cast(ContentErrorCode.E_120018);
        }
        //2.判断计划是否存在
        Teachplan teachplan = this.getById(teachPlanId);
        if(ObjectUtils.isEmpty(teachplan)){
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }
        //3.判断计划是否删除、（因为是物理删除所有无需判断）
        //4、判断是否是同一家机构
        CourseBase courseBase = courseBaseService.getById(teachplan.getCourseId());
        if(!(ObjectUtils.nullSafeEquals(companyId,courseBase.getCompanyId()))){
            ExceptionCast.cast(ContentErrorCode.E_120020);
        }
        //5.判断课程等级
        //6.判断2级下有无3级
        if(ObjectUtils.nullSafeEquals(TeachPlanEnum.SECEND_LEVEL,teachplan.getGrade())){
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getParentid,teachPlanId);

            int count = this.count(queryWrapper);
            if(count > 0){
                ExceptionCast.cast(ContentErrorCode.E_120409);
            }
        }else {
            //7.判断3级有无管理媒资数据
            LambdaQueryWrapper<TeachplanMedia> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(TeachplanMedia::getTeachplanId,teachPlanId);

            int count = mediaService.count(queryWrapper);
            if(count > 0){
                ExceptionCast.cast(ContentErrorCode.E_120414);
            }
        }

        //8.删除数据
        boolean remove = this.removeById(teachPlanId);
        if(!remove){
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }
    }

    /**
     * 添加课程计划
     */
    private TeachplanDTO createTeanchPlan(TeachplanDTO dto,Long companyId) {
        //1.判断关键数据
        //        pname,companyId
        if(ObjectUtils.isEmpty(dto.getPname()) || ObjectUtils.isEmpty(companyId)){
            ExceptionCast.cast(ContentErrorCode.E_120018);
        }
        //3.判断课程是否存在或被删除
        //5.判断是否时同一家机构
        CourseBaseDTO baseDTO = checkCourseBase(dto.getCourseId(), companyId);
        //4.判断课程审核状态
        String status = baseDTO.getAuditStatus();
        if (!CourseAuditEnum.AUDIT_UNPAST_STATUS.getCode().equals(status) &&
                !CourseAuditEnum.AUDIT_DISPAST_STATUS.getCode().equals(status)
        ) {
            ExceptionCast.cast(ContentErrorCode.E_120011);
        }

        //7.获得父级节点数据
        Teachplan parentNode = getParentNode(dto,baseDTO);
        if((TeachPlanEnum.THIRD_LEVEL).equals(parentNode.getGrade())){
            ExceptionCast.cast(ContentErrorCode.E_120415);
        }
        //8.将当前的课程计划和父级数据进行关联
        //8.1 把出入的dto转换为po
        Teachplan teachplan = TeachPlanConvert.INSTANCE.dto2entity(dto);


        //设置parentID
        teachplan.setParentid(parentNode.getId());
        // 设置当前课程计划的级别
        teachplan.setGrade(parentNode.getGrade()+1);
        //设置排序orderBy，
        LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Teachplan::getParentid,parentNode.getParentid());
        int count = this.count(queryWrapper);
        teachplan.setOrderby(count + 1);
        //9.保存课程基本信息数据
        boolean save = this.save(teachplan);

        //        判断保存的结果
        if(!save){
            //保存失败
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }
        //10.将po数据转为dto并返回
        TeachplanDTO teachplanDTO = TeachPlanConvert.INSTANCE.entity2dto(teachplan);
        return teachplanDTO;
    }

    /*
   * 获得课程计划的父级节点
   *
   *   1.课程计划的一级（根级）

       - 数据来源：课程基础信息（CourseBase）
       - 创建时机：在第一次添加二级数据时自动生成
       - 注意事项：一级父级（根级）父级 ID 值为 0

       2.课程计划的二级和三级节点

       - 数据来源：教育机构人员在页面填写的数据
       - 创建时机：教育机构人员提交课程计划数据
       - 注意事项：添加时要校验父级菜单是否存在
         - 二级在添加时
           - 父级不存在（一级）：自动创建并获得获得父级Id值
           - 父级存在（一级）：获得父级Id值
         - 三级在添加时
           - 父级不存在（二级）：抛出异常
           - 父级存在（二级）：获得父级Id值
   *
   *   机构添加的课程计划是能是：二级或三级
   *   判断新增的数据是二级或三级：依据是当前Teachplan 是否有parentId值
   *
   * */
    private Teachplan getParentNode(TeachplanDTO dto, CourseBaseDTO baseDTO) {
        //获取当前节点的父节点
        Long parentid = dto.getParentid();

        //判断当前节点是否有父节点，没有就创建父节点
        if(ObjectUtils.isEmpty(parentid )){
            //没有父节点。或父节点为0，则为2级节点
            // 获得一级课程计划
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getParentid, TeachPlanEnum.FIRST_PARENTID_FLAG);
            queryWrapper.eq(Teachplan::getCourseId,dto.getCourseId() );

            Teachplan rootNode = this.getOne(queryWrapper);

            if(ObjectUtils.isEmpty(rootNode)){
                //如果没有父节点就需要手动创建
                rootNode = new Teachplan();
                rootNode.setPname(baseDTO.getName());
                rootNode.setParentid(new Long(TeachPlanEnum.FIRST_PARENTID_FLAG));
                rootNode.setGrade(TeachPlanEnum.FIRST_LEVEL);
                rootNode.setDescription(baseDTO.getDescription());
                rootNode.setOrderby(TeachPlanEnum.FIRST_LEVEL);
                rootNode.setCourseId(baseDTO.getCourseBaseId());

                boolean result = this.save(rootNode);
                if (!result) {
                    ExceptionCast.cast(ContentErrorCode.E_120407);
                }
            }

            return rootNode;

        }else{
            //当前节点有父节点，直接根据parentId查询父节点信息即可
            Teachplan rootNode = this.getById(parentid);
            if(ObjectUtils.isEmpty(rootNode)){
                ExceptionCast.cast(ContentErrorCode.E_120407);
            }
            return rootNode;

        }

    }

    /*
     * 修改操作--课程计划
     *
     *    1.判断关键数据
     *    2.判断是否有课程数据：courseId
          3.判断课程数据是否存在
          4.判断课程审核状态
          5.判断课程是否删除
          6.判断是否是同一个机构
          7.修改课程计划数据
             有些数据是不让前端来修改
             *   parentid
             *   grade
             *   orderby
             *   courseid
           8.判断修改的结果
             依据数据：课程计划的id值 where id =xxx
             *
           9.返回结果数据 po-> dto
     *
     *
     *
     *
     * */
    private TeachplanDTO modifyTeachPlan(TeachplanDTO dto,Long companyId) {
        //1.判断关键数据
        //        pname,companyId
        if(ObjectUtils.isEmpty(dto.getPname()) || ObjectUtils.isEmpty(companyId)){
            ExceptionCast.cast(ContentErrorCode.E_120018);
        }
        //3.判断课程是否存在或被删除
        //5.判断是否时同一家机构
        CourseBaseDTO baseDTO = checkCourseBase(dto.getCourseId(), companyId);
        //4.判断课程审核状态
        String status = baseDTO.getAuditStatus();
        if (!CourseAuditEnum.AUDIT_UNPAST_STATUS.getCode().equals(status) &&
                !CourseAuditEnum.AUDIT_DISPAST_STATUS.getCode().equals(status)
        ) {
            ExceptionCast.cast(ContentErrorCode.E_120011);
        }

        // 判断课程计划是否存在
        Teachplan teachplan = this.getById(dto.getTeachPlanId());

        if (ObjectUtils.isEmpty(teachplan)) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }
        // 有些数据是不让前端来修改
        //   parentid
        dto.setParentid(teachplan.getParentid());
        //   grade
        dto.setGrade(teachplan.getGrade());
        //   orderby
        dto.setOrderby(teachplan.getOrderby());
        //   courseid
        dto.setCourseId(teachplan.getCourseId());


      // 8.判断修改的结果
        Teachplan po = TeachPlanConvert.INSTANCE.dto2entity(dto);

        boolean result = this.updateById(po);

        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }


        // 9.返回结果数据 po-> dto
        return dto;
    }

    /**
     * 把1级节点封装问树形结构
     *
     * @param rootNode 1级节点
     * @param nodeList 查询数据库的节点集合
     */
    private void createTreeNodes(TeachPlanNode rootNode, List<TeachPlanNode> nodeList) {
        //1、判断rootNode的集合是否为空
        if (ObjectUtils.isEmpty(rootNode.getChildrenNodes())) {
            rootNode.setChildrenNodes(new ArrayList<TeachPlanNode>());
        }
        //2.循环集合
        for (TeachPlanNode node : nodeList) {
            //判断当前节点和父节点是不是rootNode，如果是，就把当前节点添加到rootNode中
            if (ObjectUtils.nullSafeEquals(node.getParentid(), rootNode.getId())) {

                rootNode.getChildrenNodes().add(node);

                //递归添加
                //判断是否为3级节点如果是就不添加，因为只有三级
                if (!(TeachPlanEnum.THIRD_LEVEL.equals(node.getGrade()))) {
                    createTreeNodes(node, nodeList);
                }
            }
        }
    }

    private CourseBaseDTO checkCourseBase(Long courseId, Long companyId) {
        //1.判断关键数据
        //        courseId,companyId

        if (ObjectUtils.isEmpty(courseId) || ObjectUtils.isEmpty(companyId)) {
            //有一个为空就抛异常
            ExceptionCast.cast(ContentErrorCode.E_120018);
        }
        //3.查询课程是否存在
        CourseBaseDTO dto = courseBaseService.queryByCourseID(courseId, companyId);

        if (ObjectUtils.isEmpty(dto)) {
            ExceptionCast.cast(ContentErrorCode.E_120013);
        }
        //4.判断是否已删除
        if (ObjectUtils.nullSafeEquals(CommonEnum.DELETE_FLAG.getCodeInt(), dto.getStatus())) {
            ExceptionCast.cast(ContentErrorCode.E_120019);
        }
        //5.判读是否是同一家机构
        if (!ObjectUtils.nullSafeEquals(dto.getCompanyId(), dto.getCompanyId())) {
            ExceptionCast.cast(ContentErrorCode.E_120020);
        }

        return dto;
    }
}
